#!/usr/bin/python
#
# Copyright (C) 2008 Yu-Jie Lin
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

"""GWebmasterToolsService extends the GDataService to streamline
Google Webmaster Tools operations.

  GWebmasterToolsService: Provides methods to query feeds and manipulate items.
                          Extends GDataService.
"""

__author__ = 'livibetter (Yu-Jie Lin)'

import urllib
import gdata
import atom.service
import gdata.service
import gdata.webmastertools as webmastertools
import atom


FEED_BASE = 'https://www.google.com/webmasters/tools/feeds/'
SITES_FEED = FEED_BASE + 'sites/'
SITE_TEMPLATE = SITES_FEED + '%s'
SITEMAPS_FEED_TEMPLATE = FEED_BASE + '%(site_id)s/sitemaps/'
SITEMAP_TEMPLATE = SITEMAPS_FEED_TEMPLATE + '%(sitemap_id)s'


class Error(Exception):
    pass


class RequestError(Error):
    pass


class GWebmasterToolsService(gdata.service.GDataService):
    """Client for the Google Webmaster Tools service."""

    def __init__(self, email=None, password=None, source=None,
                 server='www.google.com', **kwargs):
        """Creates a client for the Google Webmaster Tools service.

        Args:
          email: string (optional) The user's email address, used for
              authentication.
          password: string (optional) The user's password.
          source: string (optional) The name of the user's application.
          server: string (optional) The name of the server to which a connection
              will be opened. Default value: 'www.google.com'.
          **kwargs: The other parameters to pass to gdata.service.GDataService
              constructor.
        """
        gdata.service.GDataService.__init__(
            self, email=email, password=password, service='sitemaps', source=source,
            server=server, **kwargs)

    def GetSitesFeed(self, uri=SITES_FEED,
        converter=webmastertools.SitesFeedFromString):
        """Gets sites feed.

        Args:
          uri: str (optional) URI to retrieve sites feed.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitesFeedFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitesFeed object.
        """
        return self.Get(uri, converter=converter)

    def AddSite(self, site_uri, uri=SITES_FEED,
        url_params=None, escape_params=True, converter=None):
        """Adds a site to Google Webmaster Tools.

        Args:
          site_uri: str URI of which site to add.
          uri: str (optional) URI to add a site.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitesEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitesEntry object.
        """

        site_entry = webmastertools.SitesEntry()
        site_entry.content = atom.Content(src=site_uri)
        response = self.Post(site_entry, uri,
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitesEntryFromString(response.ToString())
        return response

    def DeleteSite(self, site_uri, uri=SITE_TEMPLATE,
        url_params=None, escape_params=True):
        """Removes a site from Google Webmaster Tools.

        Args:
          site_uri: str URI of which site to remove.
          uri: str (optional) A URI template to send DELETE request.
               Default SITE_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.

        Returns:
          True if the delete succeeded.
        """

        return self.Delete(
            uri % urllib.quote_plus(site_uri),
            url_params=url_params, escape_params=escape_params)

    def VerifySite(self, site_uri, verification_method, uri=SITE_TEMPLATE,
        url_params=None, escape_params=True, converter=None):
        """Requests a verification of a site.

        Args:
          site_uri: str URI of which site to add sitemap for.
          verification_method: str The method to verify a site. Valid values are
                               'htmlpage', and 'metatag'.
          uri: str (optional) URI template to update a site.
               Default SITE_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitesEntry object.
        """

        site_entry = webmastertools.SitesEntry(
            atom_id=atom.Id(text=site_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sites-info'),
            verification_method=webmastertools.VerificationMethod(
                type=verification_method, in_use='true')
            )
        response = self.Put(
            site_entry,
            uri % urllib.quote_plus(site_uri),
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitesEntryFromString(response.ToString())
        return response


    def UpdateGeoLocation(self, site_uri, geolocation, uri=SITE_TEMPLATE,
        url_params=None, escape_params=True, converter=None):
        """Updates geolocation setting of a site.

        Args:
          site_uri: str URI of which site to add sitemap for.
          geolocation: str The geographic location. Valid values are listed in
                       http://en.wikipedia.org/wiki/ISO_3166-1_alpha-2
          uri: str (optional) URI template to update a site.
               Default SITE_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitesEntry object.
        """

        site_entry = webmastertools.SitesEntry(
            atom_id=atom.Id(text=site_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sites-info'),
            geolocation=webmastertools.GeoLocation(text=geolocation)
            )
        response = self.Put(
            site_entry,
            uri % urllib.quote_plus(site_uri),
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitesEntryFromString(response.ToString())
        return response

    def UpdateCrawlRate(self, site_uri, crawl_rate, uri=SITE_TEMPLATE,
        url_params=None, escape_params=True, converter=None):
        """Updates crawl rate setting of a site.

        Args:
          site_uri: str URI of which site to add sitemap for.
          crawl_rate: str The crawl rate for a site. Valid values are 'slower',
                      'normal', and 'faster'.
          uri: str (optional) URI template to update a site.
               Default SITE_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitesEntry object.
        """

        site_entry = webmastertools.SitesEntry(
            atom_id=atom.Id(text=site_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sites-info'),
            crawl_rate=webmastertools.CrawlRate(text=crawl_rate)
            )
        response = self.Put(
            site_entry,
            uri % urllib.quote_plus(site_uri),
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitesEntryFromString(response.ToString())
        return response

    def UpdatePreferredDomain(self, site_uri, preferred_domain, uri=SITE_TEMPLATE,
        url_params=None, escape_params=True, converter=None):
        """Updates preferred domain setting of a site.

        Note that if using 'preferwww', will also need www.example.com in account to
        take effect.

        Args:
          site_uri: str URI of which site to add sitemap for.
          preferred_domain: str The preferred domain for a site. Valid values are 'none',
                            'preferwww', and 'prefernowww'.
          uri: str (optional) URI template to update a site.
               Default SITE_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitesEntry object.
        """

        site_entry = webmastertools.SitesEntry(
            atom_id=atom.Id(text=site_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sites-info'),
            preferred_domain=webmastertools.PreferredDomain(text=preferred_domain)
            )
        response = self.Put(
            site_entry,
            uri % urllib.quote_plus(site_uri),
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitesEntryFromString(response.ToString())
        return response

    def UpdateEnhancedImageSearch(self, site_uri, enhanced_image_search,
        uri=SITE_TEMPLATE, url_params=None, escape_params=True, converter=None):
        """Updates enhanced image search setting of a site.

        Args:
          site_uri: str URI of which site to add sitemap for.
          enhanced_image_search: str The enhanced image search setting for a site.
                                 Valid values are 'true', and 'false'.
          uri: str (optional) URI template to update a site.
               Default SITE_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitesEntry object.
        """

        site_entry = webmastertools.SitesEntry(
            atom_id=atom.Id(text=site_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sites-info'),
            enhanced_image_search=webmastertools.EnhancedImageSearch(
                text=enhanced_image_search)
            )
        response = self.Put(
            site_entry,
            uri % urllib.quote_plus(site_uri),
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitesEntryFromString(response.ToString())
        return response

    def GetSitemapsFeed(self, site_uri, uri=SITEMAPS_FEED_TEMPLATE,
        converter=webmastertools.SitemapsFeedFromString):
        """Gets sitemaps feed of a site.

        Args:
          site_uri: str (optional) URI of which site to retrieve its sitemaps feed.
          uri: str (optional) URI to retrieve sites feed.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsFeedFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitemapsFeed object.
        """
        return self.Get(uri % {'site_id': urllib.quote_plus(site_uri)},
            converter=converter)

    def AddSitemap(self, site_uri, sitemap_uri, sitemap_type='WEB',
        uri=SITEMAPS_FEED_TEMPLATE,
        url_params=None, escape_params=True, converter=None):
        """Adds a regular sitemap to a site.

        Args:
          site_uri: str URI of which site to add sitemap for.
          sitemap_uri: str URI of sitemap to add to a site.
          sitemap_type: str Type of added sitemap. Valid types: WEB, VIDEO, or CODE.
          uri: str (optional) URI template to add a sitemap.
               Default SITEMAP_FEED_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitemapsEntry object.
        """

        sitemap_entry = webmastertools.SitemapsEntry(
            atom_id=atom.Id(text=sitemap_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sitemap-regular'),
            sitemap_type=webmastertools.SitemapType(text=sitemap_type))
        response = self.Post(
            sitemap_entry,
            uri % {'site_id': urllib.quote_plus(site_uri)},
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitemapsEntryFromString(response.ToString())
        return response

    def AddMobileSitemap(self, site_uri, sitemap_uri,
        sitemap_mobile_markup_language='XHTML', uri=SITEMAPS_FEED_TEMPLATE,
        url_params=None, escape_params=True, converter=None):
        """Adds a mobile sitemap to a site.

        Args:
          site_uri: str URI of which site to add sitemap for.
          sitemap_uri: str URI of sitemap to add to a site.
          sitemap_mobile_markup_language: str Format of added sitemap. Valid types:
                                          XHTML, WML, or cHTML.
          uri: str (optional) URI template to add a sitemap.
               Default SITEMAP_FEED_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitemapsEntry object.
        """
        # FIXME
        sitemap_entry = webmastertools.SitemapsEntry(
            atom_id=atom.Id(text=sitemap_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sitemap-mobile'),
            sitemap_mobile_markup_language=\
                webmastertools.SitemapMobileMarkupLanguage(
                    text=sitemap_mobile_markup_language))
        print sitemap_entry
        response = self.Post(
            sitemap_entry,
            uri % {'site_id': urllib.quote_plus(site_uri)},
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitemapsEntryFromString(response.ToString())
        return response

    def AddNewsSitemap(self, site_uri, sitemap_uri,
        sitemap_news_publication_label, uri=SITEMAPS_FEED_TEMPLATE,
        url_params=None, escape_params=True, converter=None):
        """Adds a news sitemap to a site.

        Args:
          site_uri: str URI of which site to add sitemap for.
          sitemap_uri: str URI of sitemap to add to a site.
          sitemap_news_publication_label: str, list of str Publication Labels for
                                          sitemap.
          uri: str (optional) URI template to add a sitemap.
               Default SITEMAP_FEED_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.
          converter: func (optional) Function which is executed on the server's
              response before it is returned. Usually this is a function like
              SitemapsEntryFromString which will parse the response and turn it into
              an object.

        Returns:
          If converter is defined, the results of running converter on the server's
          response. Otherwise, it will be a SitemapsEntry object.
        """

        sitemap_entry = webmastertools.SitemapsEntry(
            atom_id=atom.Id(text=sitemap_uri),
            category=atom.Category(
                scheme='http://schemas.google.com/g/2005#kind',
                term='http://schemas.google.com/webmasters/tools/2007#sitemap-news'),
            sitemap_news_publication_label=[],
            )
        if isinstance(sitemap_news_publication_label, str):
            sitemap_news_publication_label = [sitemap_news_publication_label]
        for label in sitemap_news_publication_label:
            sitemap_entry.sitemap_news_publication_label.append(
                webmastertools.SitemapNewsPublicationLabel(text=label))
        print sitemap_entry
        response = self.Post(
            sitemap_entry,
            uri % {'site_id': urllib.quote_plus(site_uri)},
            url_params=url_params,
            escape_params=escape_params, converter=converter)
        if not converter and isinstance(response, atom.Entry):
            return webmastertools.SitemapsEntryFromString(response.ToString())
        return response

    def DeleteSitemap(self, site_uri, sitemap_uri, uri=SITEMAP_TEMPLATE,
        url_params=None, escape_params=True):
        """Removes a sitemap from a site.

        Args:
          site_uri: str URI of which site to remove a sitemap from.
          sitemap_uri: str URI of sitemap to remove from a site.
          uri: str (optional) A URI template to send DELETE request.
               Default SITEMAP_TEMPLATE.
          url_params: dict (optional) Additional URL parameters to be included
                      in the insertion request.
          escape_params: boolean (optional) If true, the url_parameters will be
                         escaped before they are included in the request.

        Returns:
          True if the delete succeeded.
        """

        return self.Delete(
            uri % {'site_id': urllib.quote_plus(site_uri),
                'sitemap_id': urllib.quote_plus(sitemap_uri)},
            url_params=url_params, escape_params=escape_params)
